﻿//  --------------------------------
//  Copyright (c) AW Systems, Inc. All rights reserved.
//  This source code is made available under the terms of the Microsoft Public License (Ms-PL)
//  http://www.codeplex.com/SLAM/license
//  ---------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AWS.SLAM.Configuration;
using AWS.SLAM.SharePoint;
using AWS.SLAM.Logging;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.Diagnostics;

namespace AWS.SLAM.Activation
{
    /// <summary>
    /// The Activator consists of those actions performed by SLAM on its feature activation and deactivation.
    /// 
    /// Activator is the default implementation of SLAM's IActivator.  Activate attaches the SLAM event
    /// handlers to all Lists and Content Types configured to be managed by SLAM in SLAM's configuration.
    /// Deactivate detaches all SLAM event handlers from all configured Lists and Content Types.
    /// </summary>
    public class Activator : IActivator
    {
        #region Private State

        /// <summary>
        /// Fully qualified name of the assembly containing the SLAM event handlers
        /// </summary>
        private string _eventHandlerAssembly = "AWS.SLAM, Version=1.0.3.0, Culture=neutral, PublicKeyToken=28f8d30936a98765";

        /// <summary>
        /// Class containing the SLAM event handlers for Content Types
        /// </summary>
        private string _contentTypeEventHandlerClass = "AWS.SLAM.ContentTypeEventManager";

        /// <summary>
        /// Class containing the SLAM event handlers for Lists
        /// </summary>
        private string _listEventHandlerClass = "AWS.SLAM.ListItemEventManager";

        /// <summary>
        /// SLAM Configuration
        /// </summary>
        private IConfigurationManager _configuration;

        /// <summary>
        /// SharePoint Services for attaching and detaching event handlers in SharePoint
        /// </summary>
        private SharePointServices _services;

        /// <summary>
        /// Data Synchronizer for Activation
        /// </summary>
        private IDataSynchronizer _synchronizer;

        /// <summary>
        /// Logger for Activation
        /// </summary>
        private ILogger _logger;

        #endregion

        #region Constructors

        /// <summary>
        /// Creates instance of Activator for the given Configuration and DataSynchronizer
        /// </summary>
        /// <param name="configuration"></param>
        /// <param name="synchronizer"></param>
        public Activator(IConfigurationManager configuration, IDataSynchronizer synchronizer, ILogger logger)
        {
            _configuration = configuration;
            _logger = logger;
            _services = new SharePointServices(_logger);
            _synchronizer = synchronizer;
        }

        #endregion

        #region IActivator Members

        /// <summary>
        /// Activates SLAM by attaching all List and Content Type event handlers 
        /// as defined in SLAM's configuration.  Once all event handlers are attached 
        /// the Data Synchronizer is initiated.
        /// </summary>
        /// <returns>Boolean indicating whether Activate succeeded</returns>
        public bool Activate(string siteCollectionName)
        {
            TraceLevel currentTraceLevel = TraceLevel.Error;

            //Check if the logger is the default SqlLogger
            SqlLogger sqlLogger = _logger is SqlLogger ? ((SqlLogger)_logger) : null;
            //If it is, force it to log Activation's messages regardless of the
            //configured trace level
            if (sqlLogger != null)
            {
                currentTraceLevel = sqlLogger.Switch.Level;
                sqlLogger.Switch.Level = TraceLevel.Verbose;
            }
            _logger.LogMessage("Information", "Activation Initiated");

            DataMapping currentDataMapping = _configuration.DataMapping;
            if (!String.IsNullOrEmpty(siteCollectionName) && _configuration.DataMappings.ContainsKey(siteCollectionName))
            {
                currentDataMapping = _configuration.DataMappings[siteCollectionName];
            }

            foreach (ConfiguredType type in currentDataMapping.GetAllTypes())
            {
                //Future enhancement:
                //For the activation process to succeed currently the slam.config
                //must exist.  It is conceivable that the slam.config can used to build
                //missing types and fields on activation such that when creating a new
                //site collection that will use SLAM a config can be created first, 
                //feature activated then all types will exist and SLAM configured.

                //if (type.ContentType && !_services.VerifyContentTypeExists(type.Site, type.Name))
                //{
                //    string itemTypeName = _configuration.DataMapping.GetTypeAttribute(type.Id, type.Name, "TableName");
                //    if (String.IsNullOrEmpty(itemTypeName))
                //    {
                //        itemTypeName = type.Name;
                //    }
                //    _services.CreateContentType(type.Site, type.Name, _configuration.DataMapping.GetTypeFields("", itemTypeName));
                //}

                //If Content Type, attach Content Type handlers.  Otherwise attach List handlers.
                if (type.ContentType)
                {   
                    _services.AttachContentTypeEventHandler(type.Site, type.Name, Microsoft.SharePoint.SPEventReceiverType.ItemAdded, _eventHandlerAssembly, _contentTypeEventHandlerClass);
                    _services.AttachContentTypeEventHandler(type.Site, type.Name, Microsoft.SharePoint.SPEventReceiverType.ItemDeleting, _eventHandlerAssembly, _contentTypeEventHandlerClass);
                    _services.AttachContentTypeEventHandler(type.Site, type.Name, Microsoft.SharePoint.SPEventReceiverType.ItemUpdated, _eventHandlerAssembly, _contentTypeEventHandlerClass);

                    string typeString = "";
                    if (String.IsNullOrEmpty(type.Site))
                    {
                        typeString = type.Name;
                    }
                    else
                    {
                        typeString = String.Format("{0} - {1}", type.Site, type.Name);
                    }
                    _logger.LogMessage("Information", "Attached Event Handlers for Content Type: " + typeString);
                }
                else
                {
                    _services.DetachListEventHandlers(type.Site, type.Id, type.Name, _eventHandlerAssembly, _contentTypeEventHandlerClass);
                    
                    //Check a special case.  If a type includes an "AllSites" attribute value that is set to
                    //true event handlers will be attached to ALL Lists with the type Name.  An example of
                    //this usage is for slamming Blog Posts.  Blog lists that use the Post Content Type do not
                    //allow content types and therefore do not propogate event handlers attached to the Post
                    //content type to those Lists that use it.
                    if (!String.IsNullOrEmpty(currentDataMapping.GetTypeAttribute(type.Id, type.Name, "AllSites")) && currentDataMapping.GetTypeAttribute(type.Id, type.Name, "AllSites").ToLower() == "true")
                    {
                        _services.AttachListEventHandler("*", type.Id, type.Name, Microsoft.SharePoint.SPEventReceiverType.ItemAdded, _eventHandlerAssembly, _listEventHandlerClass);
                        _services.AttachListEventHandler("*", type.Id, type.Name, Microsoft.SharePoint.SPEventReceiverType.ItemDeleting, _eventHandlerAssembly, _listEventHandlerClass);
                        _services.AttachListEventHandler("*", type.Id, type.Name, Microsoft.SharePoint.SPEventReceiverType.ItemUpdated, _eventHandlerAssembly, _listEventHandlerClass);
                    }
                    else
                    {
                        _services.AttachListEventHandler(type.Site, type.Id, type.Name, Microsoft.SharePoint.SPEventReceiverType.ItemAdded, _eventHandlerAssembly, _listEventHandlerClass);
                        _services.AttachListEventHandler(type.Site, type.Id, type.Name, Microsoft.SharePoint.SPEventReceiverType.ItemDeleting, _eventHandlerAssembly, _listEventHandlerClass);
                        _services.AttachListEventHandler(type.Site, type.Id, type.Name, Microsoft.SharePoint.SPEventReceiverType.ItemUpdated, _eventHandlerAssembly, _listEventHandlerClass);
                    }

                    string typeString = "";
                    if (String.IsNullOrEmpty(type.Site) && String.IsNullOrEmpty(type.Id))
                    {
                        typeString = type.Name;
                    }
                    else if (String.IsNullOrEmpty(type.Site))
                    {
                        typeString = String.Format("{0} - {1}", type.Id, type.Name);
                    }
                    else if (String.IsNullOrEmpty(type.Id))
                    {
                        typeString = String.Format("{0} - {1}", type.Site, type.Name);
                    }
                    else
                    {
                        typeString = String.Format("{0} - {1} - {2}", type.Site, type.Id, type.Name);
                    }
                    _logger.LogMessage("Information", "Attached Event Handlers for List: " + typeString);
                }
            }

            //Synchronize content in SharePoint with SLAM

            SPSite site = SPControl.GetContextSite(System.Web.HttpContext.Current);
            _synchronizer.Synchronize(site.Url);

            _logger.LogMessage("Information", "Activation Complete");

            //If the logger is the default SqlLogger, return it Trace level
            //back to the configured value.
            if (sqlLogger != null)
            {
                sqlLogger.Switch.Level = currentTraceLevel;
            }

            return true;
        }

        /// <summary>
        /// Deactivates SLAM by detaching all List and Content Type event handlers 
        /// associated with SLAM.
        /// </summary>
        /// <returns>Boolean indicating whether Deactivate succeeded</returns>
        public bool Deactivate()
        {
            TraceLevel currentTraceLevel = TraceLevel.Error;

            //Check if the logger is the default SqlLogger
            SqlLogger sqlLogger = _logger is SqlLogger ? ((SqlLogger)_logger) : null;
            //If it is, force it to log Activation's messages regardless of the
            //configured trace level
            if (sqlLogger != null)
            {
                currentTraceLevel = sqlLogger.Switch.Level;
                sqlLogger.Switch.Level = TraceLevel.Verbose;
            }
            _logger.LogMessage("Information", "Deactivation Initiated");

            _services.DetachAllEventHandlers(_eventHandlerAssembly, _listEventHandlerClass, _contentTypeEventHandlerClass);

            _logger.LogMessage("Information", "Deactivation Complete");

            //If the logger is the default SqlLogger, return it Trace level
            //back to the configured value.
            if (sqlLogger != null)
            {
                sqlLogger.Switch.Level = currentTraceLevel;
            }

            //Previously SLAM detached only configured SLAM event handlers, but
            //if a type was removed from the configuration it would retain its
            //handlers and SLAM would not attempt to detach them on Deactivate
            //foreach (ConfiguredType type in _configuration.DataMapping.GetAllTypes())
            //{
            //    //If Content Type, detach Content Type handlers.  Otherwise detach List handlers.
            //    if (type.ContentType)
            //    {
            //        _services.DetachContentTypeEventHandlers(type.Site, type.Name, _eventHandlerAssembly, _contentTypeEventHandlerClass);                    
            //    }
            //    else
            //    {
            //        if (!String.IsNullOrEmpty(_configuration.DataMapping.GetTypeAttribute(type.Id, type.Name, "AllSites")) && _configuration.DataMapping.GetTypeAttribute(type.Id, type.Name, "AllSites").ToLower() == "true")
            //        {
            //            _services.DetachListEventHandlers("*", type.Id, type.Name, _eventHandlerAssembly, _listEventHandlerClass);
            //        }
            //        else
            //        {
            //            _services.DetachListEventHandlers(type.Site, type.Id, type.Name, _eventHandlerAssembly, _listEventHandlerClass);
            //        }
            //    }
            //}
            return true;
        }

        #endregion
    }
}
